﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using Microsoft.SharePoint;
using Redirection.Helpers;

namespace Redirection
{
    public class CacheService
        : ICacheService
    {
        private readonly HttpContext _ctx;
        private static readonly ReaderWriterLockSlim CacheLock = new ReaderWriterLockSlim();

        public CacheService(SPSite site,HttpContext context)
        {
            Site = site;
            _ctx = context;
        }

        private SPSite Site { get; set; }

        public Dictionary<string, RedirectionInfo> RedirectionCache
        {
            get
            {
                try
                {
                    var cacheHelper = new CacheHelper(_ctx);
                    Dictionary<string, RedirectionInfo> redirections = null;

                    if (CacheLock.TryEnterReadLock(-1))
                    {
                        redirections = cacheHelper.GetCachedRedirections(Site.RootWeb);
                    }

                    return redirections;    
                }
                finally
                {
                    if (CacheLock.IsReadLockHeld)
                        CacheLock.ExitReadLock();
                }
            }
        }

        public void CreateCache()
        {
            try
            {
                if (!CacheLock.TryEnterUpgradeableReadLock(-1)) return;
                SPSecurity.RunWithElevatedPrivileges(delegate
                    {
                        using (var elSite = new SPSite(Site.ID))
                        {
                            try
                            {
                                if (CacheLock.TryEnterWriteLock(-1))
                                {
                                    CreateRoutingTable(elSite);
                                }
                            }
                            finally
                            {
                                if (CacheLock.IsWriteLockHeld)
                                {
                                    CacheLock.ExitWriteLock();    
                                }
                            }
                        }
                    });
            }
            finally
            {
                if(CacheLock.IsUpgradeableReadLockHeld)
                CacheLock.ExitUpgradeableReadLock();
            }
            
        }

        public void Dispose()
        {
            CacheLock.Dispose();
        }

        public void ClearCache()
        {
            try
            {
                CacheLock.EnterUpgradeableReadLock();
                try
                {
                    CacheLock.EnterWriteLock();
                    var cacheHelper = new CacheHelper(_ctx);
                    cacheHelper.ClearAppCacheKey(Site.RootWeb);
                }
                finally
                {
                    if (CacheLock.IsWriteLockHeld)
                        CacheLock.ExitWriteLock();
                }
            }
            finally
            {
                if (CacheLock.IsUpgradeableReadLockHeld)
                    CacheLock.ExitUpgradeableReadLock();
            }
        }

        private void CreateRoutingTable(SPSite site)
        {
            var list = site.RootWeb.Lists.TryGetList(ConstantsService.Lists.RedirectionsList);
            if (list == null) return;

            var interval = GetCacheInterval(site);
            var routingTable = GetRedirections(list);
            
            PropertyBagHelper.SaveValues(list.ParentWeb,
                                            ConstantsService.PropertyBag.RedirectionCacheCreationKey,
                                            DateTime.UtcNow);
            
            var cacheHelper = new CacheHelper(_ctx);

            cacheHelper.SetCachedRedirections(site.RootWeb, routingTable, list, interval);
        }

        private static Dictionary<string, RedirectionInfo> GetRedirections(SPList list)
        {  
            if (list != null)
            {
                var query = new SPQuery
                    {
                        Query =
                            string.Format(
                                "<Where><Eq><FieldRef Name='ActiveRedirect' /><Value Type='Boolean'>1</Value></Eq></Where>"),
                        ViewFields =
                            "<FieldRef Name='RequestedUrl' /><FieldRef Name='DestinationUrl' /><FieldRef Name='PermanentRedirect' /><FieldRef Name='ActiveRedirect' /><FieldRef Name='Title' />",
                        ViewAttributes = "Scope='RecursiveAll'"
                    };

                var items = list.GetItems(query);

                if (items != null && items.Count > 0)
                {
                    return
                        items.Cast<SPListItem>()
                                .Select(item => new RedirectionInfo(item))
                                .ToDictionary(item => item.Source, item => item);    
                }
            }
            return new Dictionary<string, RedirectionInfo>();
        }

        private static double GetCacheInterval(SPSite site)
        {
            double interval = 300000;
            try
            {
                object value = site.RootWeb.AllProperties[ConstantsService.PropertyBag.RedirectionCacheInterval];
                double.TryParse(value.ToString(), out interval);
            }
            catch
            {
            }
            return interval;
        }
    }

    public interface ICacheService
        : IDisposable
    {
        Dictionary<string, RedirectionInfo> RedirectionCache { get; }
        void CreateCache();
        void ClearCache();
    }
}